package com.black.play.component;

import com.black.play.config.AppConfig;
import com.black.play.entity.CellValue;
import com.black.play.entity.Grid;
import com.black.play.listener.SudokuKeyListener;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;

import static com.black.play.constant.AppConstant.*;

/**
 * @author ylx
 */
public class SudokuWindow extends JFrame
{
    private final JPanel contentPane;
    private final JPanel topPane;
    private JLabel timeLabel;
    private final Grid grid = new Grid();
    private String activeCard;
    private long time;
    private Timer timer;
    public static final int LENGTH = AppConfig.getInt("window.length");
    public static final Color BACKGROUND = new Color(64, 64, 64);

    private SudokuWindow()
    {
        // 创建一个容器并设置布局
        // BorderLayout  分东南西北中布局 最多只能五个组件 会自动填充满
        setTitle("Sudoku");
        setLayout(new BorderLayout());
        // 窗口尺寸和位置调整
        setLocationAndSize();
        // 默认关闭操作
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        // 不显示标题栏
        setUndecorated(true);
        // 初始化标签用于显示得分
        topPane = new JPanel(new FlowLayout(FlowLayout.CENTER, LENGTH / 10, 0));
        initInfo();
        // 初始化卡片布局和容器  卡片布局允许添加多个  但每次只展示一个
        CardLayout cardLayout = new CardLayout();
        contentPane = new JPanel(cardLayout);
        contentPane.add(grid, GAME);
        initMenu();
        showCard(MENU);
        // 添加容器到窗口
        add(contentPane, BorderLayout.CENTER);
        // 添加键盘监听器
        addKeyListener(new SudokuKeyListener());
        // 允许键盘事件聚焦
        setFocusable(true);
        requestFocusInWindow();
    }

    /**
     * 私有构造 类加载机制 静态内部类单例
     *
     * @return MineWindow 唯一实例
     */
    public static SudokuWindow getInstance()
    {
        return SingletonHolder.instance;
    }

    private static class SingletonHolder
    {
        private static final SudokuWindow instance = new SudokuWindow();
    }

    public String getActiveCard()
    {
        return activeCard;
    }

    /**
     * 设置窗口的位置和大小。
     * 该方法通过计算屏幕尺寸来确定窗口在屏幕中的中心位置，并设置窗口的特定大小。
     */
    private void setLocationAndSize()
    {
        // 获取屏幕的尺寸
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();

        // 计算窗口在屏幕中央的 x 和 y 坐标
        int xPosition = (screenSize.width - LENGTH) >> 1;
        int yPosition = (screenSize.height - LENGTH) >> 1;

        // 设置窗口大小，高度比预定高度高出20，以提供额外的空间
        setSize(LENGTH, LENGTH + AppConfig.getInt("top.info.height"));

        // 设置窗口位置为计算得到的中央位置
        setLocation(xPosition, yPosition);
    }

    /**
     * 设置顶部信息板
     */
    private void initInfo()
    {
        // 流式居中布局 水平方向 等间隔
        topPane.setPreferredSize(new Dimension(LENGTH, 20));
        topPane.setBackground(BACKGROUND);

        // 时间
        timeLabel = new JLabel(TIME);
        timeLabel.setForeground(Color.WHITE);
        topPane.add(timeLabel);
        // 提示
        JLabel info = new JLabel(TIPS);
        info.setForeground(Color.WHITE);
        topPane.add(info);
        add(topPane, BorderLayout.NORTH);
    }

    /**
     * 设置菜单
     */
    private void initMenu()
    {
        // 主菜单 开始 设置 退出
        AppMenu mainMenu = new AppMenu();
        mainMenu.setBackground(BACKGROUND);
        mainMenu.addMenu(START, this::startGame);
        mainMenu.addMenu(SETTINGS, e -> showCard(SETTINGS));
        mainMenu.addMenu(EXIT, this::exitGame);
        contentPane.add(mainMenu, MENU);
        // 暂停界面菜单 继续 重开 返回 退出
        AppMenu pauseMenu = new AppMenu();
        pauseMenu.setBackground(BACKGROUND);
        pauseMenu.addMenu(CONTINUE, this::continueGame);
        pauseMenu.addMenu(RESTART, this::startGame);
        pauseMenu.addMenu(BACK, e -> showCard(MENU));
        pauseMenu.addMenu(EXIT, this::exitGame);
        contentPane.add(pauseMenu, PAUSE);

        //TODO settings panel  内容和颜色有待完善
        AppForm form = new AppForm();
        contentPane.add(form, SETTINGS);
    }

    /**
     * 更新时间并重绘
     */
    public void updateTime(ActionEvent... actionEvents)
    {
        time += SECOND;
        // 格式化为HH:mm:ss
        String time = getFormattedTime();
        timeLabel.setText(TIME + time);
        timeLabel.updateUI();
        topPane.updateUI();
    }

    private String getFormattedTime()
    {
        long seconds = time / 1000;
        long hours = seconds / 3600;
        long minutes = (seconds % 3600) / 60;
        long remainingSeconds = seconds % 60;
        // 格式化为HH:mm:ss
        return String.format("%02d:%02d:%02d", hours, minutes, remainingSeconds);
    }

    /**
     * 开始 初始化计时器 显示计时器 显示时间 显示提示 显示游戏界面
     *
     * @param actionEvents 事件列表 暂时不用
     */
    public void startGame(ActionEvent... actionEvents)
    {
        grid.init();
        time = 0;
        timer = new Timer(SECOND, this::updateTime);
        showCard(GAME);
    }

    /**
     * 继续游戏 恢复计时 显示游戏界面
     *
     * @param actionEvents 事件列表 暂时不用
     */
    public void continueGame(ActionEvent... actionEvents)
    {
        startTimerIfNotRun();
        showCard(GAME);
    }

    /**
     * 暂停游戏 停止计时 显示暂停界面
     *
     * @param actionEvents 事件列表 暂时不用
     */
    public void pauseGame(ActionEvent... actionEvents)
    {
        timer.stop();
        showCard(PAUSE);
    }

    /**
     * 退出游戏
     *
     * @param actionEvents 事件列表 暂时不用
     */
    public void exitGame(ActionEvent... actionEvents)
    {
        AppConfig.save();
        System.exit(0);
    }

    /**
     * 开始计时
     */
    public void startTimerIfNotRun()
    {
        if (!timer.isRunning())
        {
            updateTime();
            timer.start();
        }
    }

    /**
     * 判断是否通关
     */
    public void judgeSuccess()
    {
        if (grid.isSuccess())
        {
            timer.stop();
            JOptionPane.showMessageDialog(this, String.format(SUCCESS_TPL, getFormattedTime()), NOTICE, JOptionPane.INFORMATION_MESSAGE);
            showCard(MENU);
        }
    }

    /**
     * 判断是否错误
     *
     * @return true 错误
     */
    public boolean isError(int x, int y, CellValue value)
    {
        return grid.isError(x, y, value);
    }

    /**
     * 页面切换 激活修改
     *
     * @param cardName 卡片名
     */
    public void showCard(String cardName)
    {
        CardLayout layout = (CardLayout) contentPane.getLayout();
        layout.show(contentPane, cardName);
        this.activeCard = cardName;
    }
}